package com.isoftstone.storage;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;

import com.isoftstone.storage.authorization.AuthorizationException;
import com.isoftstone.storage.events.TransferEvent;
import com.isoftstone.storage.resource.Resource;

/**
 * 
 */
public abstract class StreamStorage extends AbstractStorage implements
		StreamingStorage {
	// ----------------------------------------------------------------------
	//
	// ----------------------------------------------------------------------

	public abstract void fillInputData(InputData inputData)
			throws TransferFailedException, ResourceDoesNotExistException,
			AuthorizationException;

	public abstract void fillOutputData(OutputData outputData)
			throws TransferFailedException;

	public abstract void closeConnection() throws ConnectionException;

	// ----------------------------------------------------------------------
	//
	// ----------------------------------------------------------------------

	public void get(String resourceName, File destination)
			throws TransferFailedException, ResourceDoesNotExistException,
			AuthorizationException {

		Resource resource = new Resource(resourceName);

		fireGetInitiated(resource, destination);

		InputStream is = getInputStream(resource);

		checkInputStream(is, resource);

		getTransfer(resource, destination, is);
	}

	protected void checkInputStream(InputStream is, Resource resource)
			throws TransferFailedException {
		if (is == null) {
			TransferFailedException e = new TransferFailedException(
					getRepository().getUrl()
							+ " - Could not open input stream for resource: '"
							+ resource + "'");
			fireTransferError(resource, e, TransferEvent.REQUEST_GET);
			throw e;
		}
	}

	protected InputStream getInputStream(Resource resource)
			throws TransferFailedException, ResourceDoesNotExistException,
			AuthorizationException {
		InputData inputData = new InputData();

		inputData.setResource(resource);

		try {
			fillInputData(inputData);
		} catch (TransferFailedException e) {
			fireTransferError(resource, e, TransferEvent.REQUEST_GET);
			cleanupGetTransfer(resource);
			throw e;
		} catch (ResourceDoesNotExistException e) {
			fireTransferError(resource, e, TransferEvent.REQUEST_GET);
			cleanupGetTransfer(resource);
			throw e;
		} catch (AuthorizationException e) {
			fireTransferError(resource, e, TransferEvent.REQUEST_GET);
			cleanupGetTransfer(resource);
			throw e;
		} finally {
			if (inputData.getInputStream() == null) {
				cleanupGetTransfer(resource);
			}
		}

		return inputData.getInputStream();
	}

	// source doesn't exist exception
	public String put(File source, String resourceName)
			throws TransferFailedException, ResourceDoesNotExistException,
			AuthorizationException {
		Resource resource = new Resource(resourceName);

		firePutInitiated(resource, source);

		resource.setContentLength(source.length());

		OutputStream os = getOutputStream(resource);

		checkOutputStream(resource, os);

		putTransfer(resource, source, os, true);
		return resource.getName();
	}

	protected void checkOutputStream(Resource resource, OutputStream os)
			throws TransferFailedException {
		if (os == null) {
			TransferFailedException e = new TransferFailedException(
					getRepository().getUrl()
							+ " - Could not open output stream for resource: '"
							+ resource + "'");
			fireTransferError(resource, e, TransferEvent.REQUEST_PUT);
			throw e;
		}
	}

	protected OutputStream getOutputStream(Resource resource)
			throws TransferFailedException {
		OutputData outputData = new OutputData();

		outputData.setResource(resource);

		try {
			fillOutputData(outputData);
		} catch (TransferFailedException e) {
			fireTransferError(resource, e, TransferEvent.REQUEST_PUT);

			throw e;
		} finally {
			if (outputData.getOutputStream() == null) {
				cleanupPutTransfer(resource);
			}
		}

		return outputData.getOutputStream();
	}

	public void getToStream(String resourceName, OutputStream stream)
			throws ResourceDoesNotExistException, TransferFailedException,
			AuthorizationException {

		Resource resource = new Resource(resourceName);

		fireGetInitiated(resource, null);

		InputStream is = getInputStream(resource);

		checkInputStream(is, resource);

		fireGetStarted(resource, null);

		getTransfer(resource, stream, is, true, Integer.MAX_VALUE);

		fireGetCompleted(resource, null);
	}

	public void putFromStream(InputStream stream, String destination)
			throws TransferFailedException, ResourceDoesNotExistException,
			AuthorizationException {
		Resource resource = new Resource(destination);

		firePutInitiated(resource, null);

		putFromStream(stream, resource);
	}

	public void putFromStream(InputStream stream, String destination,
			long contentLength, long lastModified)
			throws TransferFailedException, ResourceDoesNotExistException,
			AuthorizationException {
		Resource resource = new Resource(destination);

		firePutInitiated(resource, null);

		resource.setContentLength(contentLength);

		putFromStream(stream, resource);
	}

	protected void putFromStream(InputStream stream, Resource resource)
			throws TransferFailedException, AuthorizationException,
			ResourceDoesNotExistException {
		OutputStream os = getOutputStream(resource);

		checkOutputStream(resource, os);

		firePutStarted(resource, null);

		putTransfer(resource, stream, os, true);

		firePutCompleted(resource, null);
	}
}
